/*
* GeoVISTA Center (Penn State, Dept. of Geography)
* Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
* All Rights Researved.
*
*  
*  
*  
*  
 
 
 
* @author: jin Chen
* @date: Jul 7, 2003$
* @version: 1.0
*/
package edu.psu.geovista.app.pcp.plot.control;

import edu.psu.geovista.app.dataloader.StandAlonePcpDataLoader;
import edu.psu.geovista.app.dataloader.v1.DataLoaders;
import edu.psu.geovista.app.pcp.DfPcp;
import edu.psu.geovista.app.pcp.Pcp;
import edu.psu.geovista.app.pcp.plot.control.actions.BasicAction;
import edu.psu.geovista.app.pcp.plot.control.actions.ShowRangeCfg;
import edu.psu.geovista.app.pcp.plot.control.actions.SortOnCorrelation;
import edu.psu.geovista.app.pcp.plot.control.tools.*;
import edu.psu.geovista.app.pcp.plot.gui.BasicPcpUI;
import edu.psu.geovista.common.awt.event.dataloading.TSDataListener;
import edu.psu.geovista.ui.event.DataSetListener;

import javax.swing.*;
import javax.swing.plaf.metal.MetalLookAndFeel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.net.URL;
import java.util.*;
//edu.psu.geovista.app.pcp.plot.control.ActionManager
public class DfActionManager implements ActionMgr {
    public static final boolean DEBUG = false;

    //buttons
    static final Insets zeroInstes = new Insets(0, 0, 0, 0);
    /** Rollover toolbar icon */
    static final int ICON_ROLLOVER = 0;
    /** Selected tollbar icon */
    static final int ICON_SELECTED = 1;
    // secondary icons colors
    static final Color frameColor = MetalLookAndFeel.getPrimaryControlDarkShadow();
    static final Color rolloverBG = MetalLookAndFeel.getPrimaryControlShadow();
    static final Color selectedBG = MetalLookAndFeel.getPrimaryControl();



    //active mode buttons, can be one of button group including "select","MoveAxis","OrderAxis"....
    //private JButton activeModeBtn;
    private HashMap buttons=new HashMap();//all the button on the tool bar
    private HashMap actions=new HashMap();
    private ToolManager toolsMgr;
    BasicPcpUI ui;
    // private String[] trueFasleMode=new String[]{"DataShowAll","DataShowInvalid","DataShowPart","ShowMedianSummary","ShowObSummary"};  //all can be highlight/de-highlight by clicking
    // private String[] radioBtnMode=new String[]{"Select","OrderAxis","MoveAxis"};  //only one of them can be highlight
    private ArrayList modes=new ArrayList();


    public DfActionManager(BasicPcpUI ui) {
        this.ui =ui;
        this.toolsMgr =ui.getToolsMgr();//actionController ;
        createModeGroup();
        this.createActions() ;
        createEActions();//tmp
    }
    /**
     * set up(reset up) initial states or value for actions and button
     */
    public void reset(){
        initActions();
        initButtons();

    }
    /*******************************************************************************************************
     *                operation
     *******************************************************************************************************/
    /**
     * Invoked when an action occurs.
     * See actions' name in ActionManager
     *
     * @param actionName The name of the action to process.
     */
    public boolean processAction(String actionName) {
        boolean success=true;// 
        //System.out.println("Controller process "+actionName);
        if (actionName.equals("StartAnm") ){
            AnimationTool cmd=(AnimationTool) getCmd(BasicTool.AnmT );
            if(cmd!=null)
                cmd.start() ;
        }
        else if (actionName.equals("StepAnm") ){
            AnimationTool cmd=(AnimationTool) getCmd(BasicTool.AnmT );
            if(cmd!=null)
                cmd.step() ;
        }
        else if (actionName.equals("PauseAnm") )   {
            AnimationTool cmd=(AnimationTool) getCmd(BasicTool.AnmT );
            if(cmd!=null)
                cmd.pause() ;
        }
        else if (actionName.equals("StopAnm") ){
            AnimationTool cmd=(AnimationTool) getCmd(BasicTool.AnmT );
            if(cmd!=null)
                cmd.stop() ;
        }
        else if(actionName.equals("Select")){
            DataSelectTool scmd=(DataSelectTool)getCmd(BasicTool.DataSelectT );

            if(scmd!=null) {
                MouseObserver mob = this.ui.getMob();
                mob.setListener(scmd);
            }
            else{
                //System.out.println("Fail to set Select button");
            }

        }
        else if(actionName.equals(ACTION_HIGHLIGHT)){
            DataSelectTool scmd=(DataSelectTool)getCmd(BasicTool.DataSelectT );

            if(scmd!=null) {
                scmd.setHighLightMode(!scmd.isHightLightMode() );
                // scmd.h
            }

        }
        else if(actionName.equals("OrderAxis")){
            AxisOrderTool axOrdCmd=(AxisOrderTool)getCmd(BasicTool.AxisOrderT );
            this.ui.getMob().setListener(axOrdCmd);

        }
        else if(actionName.equals("MoveAxis")){
            AxisMoveTool axMvCmd=(AxisMoveTool)getCmd(BasicTool.AxisMoveT );
            this.ui.getMob().setListener(axMvCmd);

        }
        else if (actionName.equals("UpDownAxis")){
            AxisShowRangeTool ashCmd=(AxisShowRangeTool)getCmd(BasicTool.AxisShowRangeT );
            this.ui.getMob().setListener(ashCmd);
        }
        else if (actionName.equals("DataShowPart")){
            DataSelectTool dsc=(DataSelectTool) getCmd(BasicTool.DataSelectT ) ;
            dsc.reverseShowRangedData() ;
        }
        else if(actionName.equals("DataShowInvalid")){
            //System.out.println("DataShowInvalid");
            DataSelectTool dsc=(DataSelectTool) getCmd(BasicTool.DataSelectT ) ;
            if(dsc!=null)
                dsc.reverseShowInvalidData() ;

        }
        else if (actionName.equals(ACTION_DATASHOWALL)){
            DataSelectTool dsc=(DataSelectTool) getCmd(BasicTool.DataSelectT ) ;
            if(dsc!=null)
                dsc.updateShowBackgroundData();
        }
        else if (actionName.equals("ShowBoxplot")){
            AxisShowRangeTool axShowCmd=(AxisShowRangeTool) getCmd(BasicTool.AxisShowRangeT );

            if (axShowCmd!=null) {
                axShowCmd.reverseShowBoxPlot() ;
            }
        }
        else if (actionName.equals("AlignBoxplot")){
            AxisShowRangeTool axShowCmd=(AxisShowRangeTool) getCmd(BasicTool.AxisShowRangeT );
            if(axShowCmd!=null)
                axShowCmd.alignBoxPlot() ;
        }
        else if (actionName.equals("Reset")){
            AxisShowRangeTool axShowCmd=(AxisShowRangeTool) getCmd(BasicTool.AxisShowRangeT );
            if(axShowCmd!=null)
                axShowCmd.resetRanges() ;
        }
        else if (actionName.equals("OpenFile")){
            // getDataFromFile();
            showLoaderDialog();
        }
        else if (actionName.equals("FitWindow")){

            /* int ppw=this.ui.getPlot().getSize().width ; //outline dimension of plot bean
            int pph=this.ui.getPlot().getSize().height-Pcp.barOffset ;//-30 ;//100 is the height of toolbar and slider bar
            AxisPlaceCmd apCmd=(AxisPlaceCmd) this.getCmd(BasicCmd.AxisPlaceCmd );
            if(apCmd!=null&&ppw>0&&pph>0)
            apCmd.proposeChangePlotSize(ppw,pph, false) ;*/
            AxisPlaceTool apCmd=(AxisPlaceTool) this.getCmd(BasicTool.AxisPlaceT );
            if(apCmd!=null) apCmd.fitwindow();


        }
        else if(actionName.equals(ACTION_SHOWMEDIANSUMMRY)){
            DataSelectTool dsc=(DataSelectTool) getCmd(BasicTool.DataSelectT );
            if(dsc!=null){
                success=dsc.setMode(DataSelectTool.MODE_CategorySummary ) ;
            }
        }
        else if(actionName.equals(ACTION_ShowObsrvSumm)){

            DataSelectTool dsc=(DataSelectTool) getCmd(BasicTool.DataSelectT );
            if(dsc!=null){
                success=dsc.setMode(DataSelectTool.MODE_ObservationSummary ) ;
                //dsc.showData();
            }
        }
        else if(actionName.equals(ACTION_ShowNormal)){
            DataSelectTool dsc=(DataSelectTool) getCmd(BasicTool.DataSelectT );
            if(dsc!=null){
                success=dsc.setMode(DataSelectTool.MODE_Normal ) ;
                //dsc.showData() ;
                //System.out.println("NormalMode");
            }
        }
        else if(actionName.equals("ShowCorrelation")){
            AxisShowRangeTool cmd=(AxisShowRangeTool) getCmd(BasicTool.AxisShowRangeT);
            cmd.showCorrelation();
        }

        if(success)
            this.setMode(actionName);  //bugyy, what if it failed,
        return success;
    }

    public BasicTool getCmd(String name) {
        return this.toolsMgr.getCmd(name);
    }
    private void showLoaderDialog(){
        Window w;
        Pcp pcp=this.ui.getPlot();
        w=SwingUtilities.getWindowAncestor((JComponent)pcp);
        ///w.getcon
        DataLoaders dl;
        try {
            // JSwingUtils.showWaitCursor(w,null);
            dl=new StandAlonePcpDataLoader();

            // Pcp pcp=this.ui.getPlot();
            dl.addDataSetListener((DataSetListener)pcp);
            dl.addTsDataListener((TSDataListener)pcp);
            dl.setRootWindow(w);// 
            dl.startWizard() ;

        } finally {
            //JSwingUtils.showNormalCursor(w);// 
        }

    }
    /**
     * Invoked by action class to perform corresponding action
     * @param actionName
     */
    /* public void processAction(String actionName) {


    actionController.processAction(actionName);
    this.setMode(actionName);
    }*/
    /**
     * change the appearance of buttons according to mode
     * @param actionName
     */
    private void setMode(String actionName){

        for (Iterator iterator = modes.iterator(); iterator.hasNext();) {
            Mode mode = (Mode) iterator.next();
            if(mode.contains(actionName) ){
                if(mode.getType()==Mode.TYPE_RadioBtn ){
                    for (Iterator iter = mode.iterator(); iter.hasNext();) {
                        String btnName = (String) iter.next();
                        JButton btn=(JButton) buttons.get(btnName);
                        btn.setSelected(false);
                    }
                    ((JButton)buttons.get(actionName)).setSelected(true);
                }
                else if(mode.getType()==Mode.TYPE_TrueFalse ){
                    JButton b=(JButton) this.buttons.get(actionName);
                    boolean s=b.isSelected() ;
                    b.setSelected(!s);
                }
            }
        }

    }
    /*******************************************************************************************************
     *                create buttons and actions
     *******************************************************************************************************/
    /**
     * init buttons' state
     */
    protected void initButtons(){
        //* initially deselected. If reloading data, reset all button to be deselected
        Collection btns = this.buttons.values();
        for (Iterator iterator = btns.iterator(); iterator.hasNext();) {
            JButton btn = (JButton) iterator.next();
            btn.setSelected(false);
        }
        //* Following buttons are initially selected
        JButton showall = this.getButton(ACTION_DATASHOWALL);
        showall.setSelected(true);// 
        JButton highlight = this.getButton(ACTION_HIGHLIGHT);
        highlight.setSelected(true);
        //highlight.doClick();  //highlight.setSelected(true);


    }
    /**
     * init action's state
     */
    protected void initActions(){

    }
    private void createModeGroup(){
        String[] showModeG=new String[]{ACTION_DATASHOWALL,"DataShowInvalid","DataShowPart",ACTION_HIGHLIGHT};//    "Highlight"
        Mode showMode=new Mode(showModeG);
        showMode.setType(Mode.TYPE_TrueFalse ); //each of the buttons can be highlight/de-highlight by clicking
        modes.add(showMode);


        String[] dataModeG=new String[] {ACTION_SHOWMEDIANSUMMRY,ACTION_ShowObsrvSumm,ACTION_ShowNormal};
        Mode dataMode=new Mode(dataModeG);
        dataMode.setType(Mode.TYPE_RadioBtn ); //only one of the buttons can be highlight
        modes.add(dataMode);

        String[] opModeG=new String[] {"Select","OrderAxis","MoveAxis","UpDownAxis"};
        Mode opMode=new Mode(opModeG);
        opMode.setType(Mode.TYPE_RadioBtn );
        modes.add(opMode);


    }

    /**
     * Get the action for the given action name.
     * If action does not exists create new one.
     *
     * Some information is common for menuitems and buttons, so it is stored in action class.
     *
     */
    private  void createActions() {
        // 
        //Action Name - Tooltip pair   "DataShowAll"
        String[] actionAttrbs={   ACTION_DATASHOWALL,"Show all data as background",
                                  "DataShowPart","Show Ranged data",
                                  "DataShowInvalid","Show missing data",
                                  "Select","Select data by brushing(dragging mouse)",
                                  ACTION_HIGHLIGHT,"Highlight data",
                                  "StartAnm","Start animation",
                                  "StepAnm","Manually step animation",
                                  "PauseAnm","Pause animation",
                                  "StopAnm","Stop animation",
                                  "OrderAxis","Order axis",
                                  "MoveAxis","Move axis",
                                  "UpDownAxis","Move axis up and down",
                                  "ShowBoxplot","Show Boxplot",
                                  "AlignBoxplot","Align Boxplot",
                                  "ShowCorrelation","Show correlation between two axes",
                                  "Reset","Reset tick range to default value",
                                  "OpenFile", "Open a data file",
                                  "FitWindow", "Fit plot to window",
                                  ACTION_SHOWMEDIANSUMMRY,"show category median summary",
                                  ACTION_ShowObsrvSumm,"show observation median summary",
                                  ACTION_ShowNormal,"Normal mode",
                                  "ScreenShoot","Screenshot",
                                    "ShowRangeCfg", "Show Axis Range"

        };

        for (int i=0;i<actionAttrbs.length ;i=i+2){
            String actName=actionAttrbs[i];
            String tooltip=actionAttrbs[i+1];

            PAction action=this.createAction(actName,tooltip);
            this.actions.put(actName,action);
            //action.setEnabled(true);
            JButton btn=this.createToolbarButton(action);
            /*if(actName.equals(ACTION_DATASHOWALL)||actName.equals(ACTION_HIGHLIGHT)){
            btn.setSelected(true);//by default, NOT show all data

            }*/

            this.buttons.put(actName,btn);
            //this.actions.put(actName,action);

        }
        DfPcp pcp = (DfPcp) this.toolsMgr.ui.getPlot();
        boolean standalone=pcp.isStandAlone() ;
        this.setActionEnable("OpenFile",standalone);


    }
    /**
     * todo
     * Need change all responses to action class.Not finish yet.
     * From now on, new action will be put here
     * Need implement ClassLoader to automatically load the action classes
     *  
     */
    private void createEActions() {
         createAction(ShowRangeCfg.class );
       /* createAction(ShowRangeCfg.class );
        createAction(ShowLocCfg.class );
        //createAction(ShowCfg.class );
        createAction(ShowHistgrm.class );*/


        BasicAction sortCorrAction = createAction(SortOnCorrelation.class);
        JButton sortCorrBtn = createToolbarButton(sortCorrAction);
        this.buttons.put(sortCorrAction.getValue(Action.NAME ),sortCorrBtn);

        /* BasicAction showRangeAction = createAction(ShowRangeCfg.class);
        JButton showRangeBtn = createToolbarButton(showRangeAction);
        this.buttons.put(showRangeAction.getValue(Action.NAME ),showRangeBtn);*/
        BasicAction showRangeAction = createAction(ShowRangeCfg.class);
        JButton showRangeBtn = createToolbarButton(showRangeAction);
        this.buttons.put(showRangeAction.getValue(Action.NAME ),showRangeBtn);


    }
    private BasicAction createAction(Class cls){

        ImageIcon icon=null;
        String name =null;
        Object o=null ;
        try {
            o = cls.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IllegalAccessException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        if(o!=null && o instanceof BasicAction){
            BasicAction action=(BasicAction) o;

            action.setPcpUI(this.ui);

            name =DfActionManager.getActionID(action.getClass());
            icon = DfActionManager.getImageIcon(name+".gif");
            action.putValue(PAction.SMALL_ICON, icon);
            action.putValue(Action.NAME,name);     //todo need remove it
           // JMenuBar

            this.actions.put(name,action);
            return action;

        }
        return null;

    }
    /**
     *
     * @param  name    the action name.
     * @param tooltip  description of the action
     * @return         an action object
     */
    public PAction createAction(String name, String tooltip) {
        PAction action = new PAction(name);


        // set image

        ImageIcon icon=DfActionManager.getImageIcon(name+".gif");//By default, image icon=action name
        action.putValue(PAction.SMALL_ICON, icon);

        // set tooltip
        action.putValue(PAction.SHORT_DESCRIPTION, tooltip);

        // update enabled/disabled state of newly created action
        if (toolsMgr != null)
            action.setEnabled(getActionState(name) == ACTION_ENABLED);
        return action;
    }

    public void setActionEnable(String name, boolean enable) {
        JButton btn=(JButton) this.buttons.get(name);
        if(btn!=null)
            btn.setEnabled(enable);
    }

    public Action getAction(String actName) {
        Object o = actions.get(actName);
        return (Action) o;
    }
    public int getActionState(String actionName) {
        return ActionMgr.ACTION_ENABLED;
    }
    /**
     *  
     * @param cls
     * @return
     */
    public static String getActionID(Class cls){
        return cls.getSimpleName() ;
    }
    /**
     * Get the action for the given action name.
     *
     * @param name 		The action name.
     *
     * @return an action object.
     */
    /*public Action getAction(String name){
    return (Action)actions.get(name);
    }*/
    /*******************************************************************************************************
     *                support
     *******************************************************************************************************/

    /**
     * This method creates a toolbar button using action descriptor for given action name.
     *
     * @param action The action used for this menu.
     *
     * @return new toolbar button.
     */
    private  JButton createToolbarButton(AbstractAction action) {

        //Action action = getAction(actionName);
        String actionName=(String) action.getValue(PAction.NAME );

        JButton btn = new JButton();

        btn.setAction(action);
        btn.setText(null);
        btn.setMargin(zeroInstes);
        btn.setMnemonic(0);
        btn.setActionCommand(actionName);
        btn.setBorderPainted(false);
        btn.setFocusPainted(false);
        btn.setRequestFocusEnabled(false);
        btn.setRolloverEnabled(true);
        //btn.addMouseListener(MenuMouseAdapter.getInstance());
        Icon icon = btn.getIcon();
        if (icon != null) {
            btn.setRolloverEnabled(true);
            btn.setRolloverIcon(new DfActionManager.ExtraIcon(icon, ICON_ROLLOVER));
            btn.setPressedIcon(new DfActionManager.ExtraIcon(icon, ICON_SELECTED));
            btn.setSelectedIcon(btn.getPressedIcon());
        }

        //btn.setPreferredSize(new Dimension(20,20));
        btn.setName(actionName);// 
        btn.setText(null);// 
        return btn;
    }
    public JButton getButton(String btnName){
        return (JButton) this.buttons.get(btnName);
    }
    // loading images
    public static ImageIcon getImageIcon(String name) {
        String fullName=DfPcp.IMAGES+name;
        ClassLoader cl=DfActionManager.class.getClassLoader() ;
        URL url=cl.getResource(fullName);
        if (url == null) {
            //dp{
            if (DEBUG){
                System.out.println("image "+fullName+" not found");
            }//dp}

            return null;
        }
        return new ImageIcon(url);
    }
    public class PAction extends AbstractAction {

        /**
         * Constructor declaration
         *
         * @param name 		The action name
         */
        public PAction(String name) {
            super(name);
            this.putValue(ACTION_COMMAND_KEY, name);
        }

        /**
         * Invoked when an action occurs.
         */
        public void actionPerformed(ActionEvent e) {
            processAction((String) this.getValue(ACTION_COMMAND_KEY) );

            //processAction(e.getActionCommand());


        }

    }
    class ExtraIcon implements Icon {

        /** Primary icon */
        private Icon icon;
        /** type of the secondary icon */
        private int iconType;

        /** Creates an extra icon based on the provided icon */
        public ExtraIcon(Icon icon, int iconType) {
            this.icon = icon;
            this.iconType = iconType;
        }

        /**
         * Draw the icon at the specified location.
         * Uses iconType to determine how to modify original icon.
         */
        public void paintIcon(Component c, Graphics g, int x, int y) {
            Color bgColor = (iconType == ICON_ROLLOVER) ? rolloverBG : selectedBG;

            g.setColor(bgColor);
            g.fillRect(x - 3, y - 3, getIconWidth() + 5, getIconHeight() + 5);
            g.setColor(frameColor);
            g.drawRect(x - 3, y - 3, getIconWidth() + 5, getIconHeight() + 5);

            icon.paintIcon(c, g, x + iconType, y + iconType);
        }

        /**
         * Returns the icon's width.
         *
         * @return an int specifying the fixed width of the icon.
         */
        public int getIconWidth() {
            return icon.getIconWidth();
        }

        /**
         * Returns the icon's height.
         *
         * @return an int specifying the fixed height of the icon.
         */
        public int getIconHeight() {
            return icon.getIconHeight();
        }
    }

    class Mode {
        public static final int TYPE_TrueFalse=1; //each of the buttons can be highlight/de-highlight by clicking
        public static final int TYPE_RadioBtn=2;//only one of them can be highlight

        private int type;
        private HashSet elements=new HashSet();

        public Mode() {
        }
        public Mode(String[] names) {
            this.addGroup(names);
        }
        public void addGroup(String[] names) {

            for (int i=0;i<names.length ;i++){
                this.add(names[i]);
            }
        }
        public void add(String name) {
            elements.add(name);

        }

        public boolean contains(String name){
            return elements.contains(name);

        }

        public Iterator iterator(){
            return elements.iterator() ;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }
    }




}
